/****************************************************************

kernel/fault.c
Fault code for AWOS

Copyright (c) 2006 Andrew Wilcox. All rights reserved.

Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:

1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.

3. The end-user documentation included with the redistribution,
if any, must include the following acknowledgment:

"This product includes software developed by Andrew Wilcox for
AWOS. (http://code.google.com/p/awos)."

Alternately, this acknowledgment may appear in the software
itself, if and wherever such third-party acknowledgments normally
appear.

4. The names "AWOS" and "Andrew Wilcox" must not be used to
endorse or promote products derived from this software without
prior written permission. For written permission, please contact
Andrew.R.Wilcox@Gmail.com.

5. Products derived from this software may not be called "AWOS"
 nor may "AWOS" appear in their name, without prior written
 permission of Andrew Wilcox.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ANDREW WILCOX BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.

*****************************************************************/

#include <kernel.h>
#include <string.h>

#define	PGF_PRESENT	0x01
#define	PGF_WRITE	0x02
#define	PGF_USER	0x04
#define	PGF_RSVD	0x08
#define	PGF_IF		0x10

struct frame
{
	struct frame *next;
	void *address;
};

void decide(int);

char *exception_messages[] =
{
	"Division by Zero", "Debug", "Non-Maskable Interrupt", "Breakpoint",
	"INTO Detected Overflow", "Out of Bounds", "Invalid Opcode", "No Coprocessor",
	"Double Fault", "Coprocessor Segment Overrun", "Bad TSS", "Segment Not Present",
	"Stack Fault", "General Protection Fault", "Page Fault", "Unknown Interrupt",
	"Coprocessor Fault", "Alignment Check (486+)", "Machine Check (Pentium/586+)",
	"SIMD floating-point exception", "DOS syscall", "Reserved", "Reserved", "Reserved",
	"Reserved", "Reserved", "Reserved", "Reserved", "Reserved", "Windows syscall", "Reserved",
	"Reserved", "Kernel Detected Fault"
};

char *meaning[] =
{
	"An application attempted to divide by zero.",
	"An application raised a debug exception.",
	"The information you requested is not available at this time.",
	"An application executed the \"INT 3\" instruction.",
	"The information you requested is not available at this time.",
	"The information you requested is not available at this time.",
	"An application tried to execute an invalid instruction.",
	"An application tried to execute an instruction that\nrequires a coprocessor.  Your system does not have a coprocessor.",
	"The kernel crashed trying to execute another exception handler.",
	"The 387 coprocessor detected a page/segment violation trying to\nexecute an instruction.",
	"The processor detected an error trying to switch tasks.",
	"An application needed memory that was not available, and the\nAWOS kernel could not give the memory to the requesting application.",
	"There was not enough memory free to allocate stack space,\nmemory too high was referenced to the SS register, or\na \"Segment Not Present\" fault was detected while trying to load the SS register.",
	"The segment limit was reached trying to access code or data,\ntrying to execute code in a segment that is not executable,\nwriting to a code or read-only data segment,\nloading a kernel segment into a user application, etc...\nThere are many different meanings and this is one of the most common exceptions\non the personal computer.",
	"This indicates memory that an application requested was not\npresent in physical memory.",
	"This error should not occur because it is not defined in the\nIA-32 architecture.",
	"The x86 FPU detected a floating-point error.",
	"The data an application tried to access memory that was not\naligned correctly while alignment checking was on.",
	"The processor detected an internal machine or bus error, or\nan external agent detected a bus error.",	
	"The processor detected an SSE/SSE2/SSE3 SIMD floating-point\nexception.  There are six types:  Invalid operation,\nDivide by Zero, Denormal Operand, Numeric Overflow,\nand Result Not Exact (Precision).",
	"The DOS emulator was not started, and you ran an application\nthat requires it.",
	"This error should not occur because it is not defined in the\nIA-32 architecture.",
	"This error should not occur because it is not defined in the\nIA-32 architecture.",
	"This error should not occur because it is not defined in the\nIA-32 architecture.",
	"This error should not occur because it is not defined in the\nIA-32 architecture.",
	"This error should not occur because it is not defined in the\nIA-32 architecture.",
	"This error should not occur because it is not defined in the\nIA-32 architecture.",
	"This error should not occur because it is not defined in the\nIA-32 architecture.",
	"This error should not occur because it is not defined in the\nIA-32 architecture.",
	"The Windows emulator was not started.",
	"This error should not occur because it is not defined in the\nIA-32 architecture.",
	"This error should not occur because it is not defined in the\nIA-32 architecture.",
	"The AWOS kernel detected an unrecoverable error."
};

char *mean32[] = 
{
	"An unknown error occurred in the kernel.", "Kernel doesn't support function.",
	"Invalid MBR detected.", "Could not restart the computer.", "malloc/heap error"
};

/*char *what2do [] = 
{
	"* Press ENTER to attempt to continue the application.",
	"Not available", "Not available", "Not available", "Not available",
	"Not available", "Not available", "Not available", "Not available",
	"Not available", "Not available", "Not available", "Not available",
	"Not available", "* Press any key to restart your computer.", "Not available", "Not available",
	"Not available", "Not available", "Not available", "Not available",
	"Not available", "Not available", "Not available", "Not available",
	"Not available", "Not available", "Not available", "Not available",
	"Not available", "Not available", "Not available", "Not available",
	"Not available", "Not available", "Not available", "Not available",
	"Not available", "Not available", "Not available", "Not available",
	"Not available", "Not available", "Not available", "Not available",
	"Not available", "Not available", "* Press ENTER to start the Windows emulator\n* Press ESC to cancel the request.", "Not available",
	"Not available"
	
};

char *what32[] =
{
	"Press any key to restart your computer.  If the error occurs again, contact support.",
	"Run 'FDISK /MBR' from an MS-DOS startup disk.",
	"Turn the computer off and back on manually.",
	"Press any key to restart your computer.  If the error occurs again, you may have bad memory."
};*/

void fault_handler(struct regs *r)
{
	struct frame *stack;
	struct KiMemStats *mem;
	int cr0, cr2, cr3, cr4, i;
	
	if(r->int_no != 0x08)
		mem = (struct KiMemStats *)kmalloc(sizeof(struct KiMemStats));
	
	asm("mov %%cr0, %0" : "=r"(cr0));
	asm("mov %%cr2, %0" : "=r"(cr2));
	asm("mov %%cr3, %0" : "=r"(cr3));
	asm("mov %%cr4, %0" : "=r"(cr4));
	
	if(r->int_no < 0x33)
	{
		KiVgaSetTextColor(0x0F, 0x01);
		clrscr();
		if(r->cs <= 0x0008)
			printf("An exception %02X (%s) has occurred with error code %i in\
			 the AWOS kernel (%04X:%08X).\n\n", r->int_no,\
			exception_messages[r->int_no], r->err_code, r->cs, *r->eip);
		else
			printf("An exception %02X (%s) has occurred with error code %i at\
			 %04X:%08X.\n\n", r->int_no, exception_messages[r->int_no],\
			r->err_code, r->cs, *r->eip);
		switch(r->int_no)
		{
			case 0x06:
			/******** #UD -- UNDEFINED OPCODE ********/
				printf("Bytes at CS:EIP:\n");
				for(i = 0; i < 10; i++) printf("%02X ", *(char *)r->eip+i);
				break;
			/******** END #UD ********/
			case 0x0E:
			/******** #PF -- PAGE FAULT ********/
				printf("Memory at location %08X.  Reason for fault:\n", cr2);
				if(r->err_code &= PGF_PRESENT)
				{
					printf("The memory was not present.\n");
				}
			
				if(r->err_code &= PGF_WRITE)
				{
					printf("The memory could not be written.\n");
				} else {
					printf("The memory could not be read.\n");
				}
			
				if(r->err_code &= PGF_USER)
				{
					printf("The error occurred in user mode.\n");
				} else {
					printf("The error occurred in supervisor mode.\n");
				}
			
				if(r->err_code &= PGF_RSVD)
				{
					printf("The error was caused by reserved bits set to 1.\n");
				}
			
				if(r->err_code &= PGF_IF)
				{
					printf("The error was caused by an instruction fetch.\n");
				}
				break;
			/******** END #PF ********/
			case 0x32:
			/******** AWOS KERNEL ERROR ********/
				printf("WHAT IT MEANS: %s\n", mean32[r->eax]);
				break;
			/******** END AWOS KERNEL ERROR ********/
		};
		
		printf("\n====STACK DUMP====\n");
		printf("DS: %04X      ES: %04X      FS: %04X      GS: %04X\n", r->ds, r->es, r->fs, r->gs);
		printf("EDI:%08X  ESI:%08X  EBP:%08X  ESP:%08X\n", r->edi, r->esi, r->ebp, r->esp);
		printf("EAX:%08X  EBX:%08X  ECX:%08X  EDX:%08X\n", r->eax, r->ebx, r->ecx, r->edx);
		printf("CR0:%08X  CR2:%08X  CR3:%08X  CR4:%08X\n", cr0, cr2, cr3, cr4);
		printf("CS: %04X      EIP:%08X  FLG:%08X  USP:%08X\n", r->cs, *r->eip, r->eflags, r->useresp);
		printf("SS: %08X  Instruction at %04X:%08X\n\n", r->ss, r->cs, *r->eip);
		
		stack = (struct frame *)r->ebp;
		
		if(r->int_no != 0x08)
		{
#if 0
			KiGetKernelMemStats(mem);
			
			printf("====HEAP DUMP====\n==KERNEL==\nTotal Mem    Mem Usage    Mem Free    %% free\n");
			printf("%10d   %10d   %10d  %3d %%\n", mem->TotalMemory, mem->CurAlloc, mem->TotalFree, ((mem->TotalFree / mem->TotalMemory) * 100));
			
			KiGetUserMemStats(mem);
			
			printf("==USER==\nTotal Mem    Mem Usage    Mem Free    %% free\n");
			printf("%10d   %10d   %10d  %3d %%\n\n", mem->TotalMemory, mem->CurAlloc, mem->TotalFree, (mem->TotalFree != 0 ? (mem->TotalFree / mem->TotalMemory) : 0));
#endif
		} else {
			printf("total kernel allocations: %i", KiGetNumAlloc(FALSE));
			printf("total user allocations:   %i", KiGetNumAlloc(TRUE));
		}

		printf("====Call stack====\n");
		while(stack->next) {
			printf("called from %8p\n", stack->address);
			stack = stack->next;
		}
	
		decide(r->int_no); //halt?
	}
	return;
}

void decide(int intno)
{
	switch(intno)
	{
		case 0x08:
			/* double fault -- type ABORT */
			asm("hlt");
			break;
		case 0x09:
			/* coprocessor segment overrun -- type ABORT */
			asm("hlt");
			break;
		case 0x12:
			/* machine check -- type ABORT */
			asm("hlt");
			break;
		default:
			asm("hlt");
			break;
	};
	
	return;
}
